/*
 *   Copyright 2012, Thomas Kerber
 *
 *   Licensed under the Apache License, Version 2.0 (the "License");
 *   you may not use this file except in compliance with the License.
 *   You may obtain a copy of the License at
 *
 *       http://www.apache.org/licenses/LICENSE-2.0
 *
 *   Unless required by applicable law or agreed to in writing, software
 *   distributed under the License is distributed on an "AS IS" BASIS,
 *   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *   See the License for the specific language governing permissions and
 *   limitations under the License.
 */
package milk.skimmed;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.Serializable;
import java.util.Stack;
import java.util.logging.Level;

/**
 * Manages fallback histories of configurations.
 * 
 * Maintains a stack of configurations. The top of the stack is always to
 * current one, all beyond that may be fallen back to.
 * 
 * An empty configuration is maintained at all times.
 * @author Thomas Kerber
 * @version 1.0.0
 * @see milk.skimmed.Configuration
 */
public class ConfigFallbackHistory implements Serializable{
    
    private static final long serialVersionUID = 8316025100889879041L;
    
    /**
     * The instance.
     */
    public static final ConfigFallbackHistory INSTANCE;
    
    static{
        try{
            Skimmed.ensureVersionCorrectness();
        }
        catch(IOException e){
            Util.logger.log(Level.SEVERE, "", e);
            Callback.issueError("IO error occurred during version " +
                    "checks. See log for details.");
        }
        File seriFile = new File(Skimmed.MILK_ROOT, "confs");
        if(!seriFile.exists())
            INSTANCE = new ConfigFallbackHistory();
        else{
            try{
                ObjectInputStream in = new ObjectInputStream(
                        new FileInputStream(seriFile));
                INSTANCE = (ConfigFallbackHistory)in.readObject();
                in.close();
            }
            catch(IOException e){
                throw new Error(e);
            }
            catch(ClassNotFoundException e){
                throw new Error(e);
            }
        }
    }
    
    /**
     * The configuration stack.
     */
    private Stack<Configuration> configs = new Stack<Configuration>();
    
    /**
     * Dumps the config history to the save file.
     * 
     * Issues a warning to the log if it fails.
     */
    public void dump(){
        
        try{
            File seriFile = new File(Skimmed.MILK_ROOT, "confs");
            ObjectOutputStream out = new ObjectOutputStream(new FileOutputStream(
                    seriFile));
            out.writeObject(this);
            out.close();
        }
        catch(IOException e){
            Util.logger.log(Level.WARNING, "Configs couldn't be saved.", e);
        }
    }
    
    /**
     * 
     * @return The config stack.
     */
    public Stack<Configuration> getConfigs(){
        return configs;
    }
    
    /**
     * Creates a new fallback point if applicable. Should be called only if:
     * 
     *  - The current config can be compiled.
     *  - The current config passes basic sanity tests.
     */
    public void add(){
        
        if(configs.size() == 1 && configs.get(0).equals(new Configuration()))
            return;
        else if(configs.size() >= 2 && configs.get(configs.size() - 1).equals(
                configs.get(configs.size() - 2)))
            return;
        configs.push(Skimmed.CONFIG.clone());
        Skimmed.CONFIG = configs.peek();
        dump();
        
    }
    
    /**
     * Falls back to the next newest configuration.
     * 
     * Note: You should probably have the user confirm before you use this.
     * @return The "new" active configuration.
     */
    public Configuration fallback(){
        
        // There is *always* an empty fallback config.
        if(configs.size() == 0)
            configs.add(new Configuration());
        configs.pop();
        configs.peek().killGhosts();
        Skimmed.CONFIG = configs.peek();
        dump();
        return configs.peek();
        
    }
    
    /**
     * 
     * @return The currently active configuration.
     */
    public Configuration get(){
        // There is *always* an empty fallback config.
        if(configs.size() == 0){
            configs.add(new Configuration());
        }
        return configs.peek();
    }
    
}
